1   /*
2    * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 6204469
27   * @summary Test that Open MBean attributes and parameters check constraints
28   * @author Eamonn McManus
29   * @run clean ConstraintTest
30   * @run build ConstraintTest
31   * @run main ConstraintTest
32   */
33  
34  import java.util.*;
35  import javax.management.*;
36  import javax.management.openmbean.*;
37  
38  public class ConstraintTest {
39      private static String failure;
40  
41      public static void main(String[] args) throws Exception {
42          for (Object[][] test : tests) {
43              if (test.length != 4) {
44                  throw new Exception("Test element has wrong length: " +
45                                      Arrays.deepToString(test));
46              }
47  
48              if (test[0].length != 4) {
49                  throw new Exception("Test constraints should have size 4: " +
50                                      Arrays.deepToString(test[0]));
51              }
52              Object defaultValue = test[0][0];
53              Comparable<?> minValue = (Comparable<?>) test[0][1];
54              Comparable<?> maxValue = (Comparable<?>) test[0][2];
55              Object[] legalValues = (Object[]) test[0][3];
56              System.out.println("test: defaultValue=" + defaultValue +
57                                 "; minValue=" + minValue +
58                                 "; maxValue=" + maxValue +
59                                 "; legalValues=" +
60                                 Arrays.deepToString(legalValues));
61  
62              if (test[1].length != 1) {
63                  throw new Exception("OpenType list should have size 1: " +
64                                      Arrays.deepToString(test[1]));
65              }
66              OpenType<?> openType = (OpenType<?>) test[1][0];
67  
68              Object[] valid = test[2];
69              Object[] invalid = test[3];
70  
71              System.out.println("...valid=" + Arrays.deepToString(valid));
72              System.out.println("...invalid=" + Arrays.deepToString(invalid));
73  
74              test(openType, defaultValue, minValue, maxValue, legalValues,
75                   valid, invalid);
76          }
77  
78          if (failure == null)
79              System.out.println("Test passed");
80          else
81              throw new Exception("TEST FAILED: " + failure);
82      }
83  
84      private static <T> void test(OpenType<T> openType, Object defaultValue,
85                                   Comparable<?> minValue,
86                                   Comparable<?> maxValue, Object[] legalValues,
87                                   Object[] valid, Object[] invalid)
88              throws Exception {
89          /* This hack is needed to avoid grief from the parameter checking
90             in the OpenMBean*InfoSupport constructors.  Since they are defined
91             to check that the defaultValue etc are of the same type as the
92             OpenType<T>, there is no way to pass a defaultValue etc when
93             the type is OpenType<?>.  So either you have to write plain
94             OpenType, and get unchecked warnings for every constructor
95             invocation, or you do this, and get the unchecked warnings just
96             here.  */
97          test1(openType, (T) defaultValue, (Comparable<T>) minValue,
98                (Comparable<T>) maxValue, (T[]) legalValues, valid, invalid);
99      }
100 
101     private static <T> void test1(OpenType<T> openType, T defaultValue,
102                                   Comparable<T> minValue,
103                                   Comparable<T> maxValue, T[] legalValues,
104                                   Object[] valid, Object[] invalid)
105             throws Exception {
106 
107         if (legalValues != null && (minValue != null || maxValue != null))
108             throw new Exception("Test case has both legals and min/max");
109 
110         if (defaultValue == null && minValue == null && maxValue == null &&
111             legalValues == null) {
112             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
113                                                    true, true, false),
114                  valid, invalid);
115             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
116                                                    true, true, false, nullD),
117                  valid, invalid);
118             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
119                                                    true, true, false, emptyD),
120                  valid, invalid);
121             test(new OpenMBeanParameterInfoSupport("name", "descr", openType),
122                  valid, invalid);
123             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
124                                                    nullD),
125                  valid, invalid);
126             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
127                                                    emptyD),
128                  valid, invalid);
129         }
130 
131         if (minValue == null && maxValue == null && legalValues == null) {
132             Descriptor d = descriptor("defaultValue", defaultValue);
133             test(new OpenMBeanAttributeInfoSupport("blah", "descr", openType,
134                                                    true, true, false, d),
135                  valid, invalid);
136             test(new OpenMBeanAttributeInfoSupport("blah", "descr",
137                                                    openType, true, true,
138                                                    false, defaultValue),
139                  valid, invalid);
140             test(new OpenMBeanParameterInfoSupport("blah", "descr", openType,
141                                                    d),
142                  valid, invalid);
143             test(new OpenMBeanParameterInfoSupport("blah", "descr", openType,
144                                                    defaultValue),
145                  valid, invalid);
146         }
147 
148         if (legalValues == null) {
149             Descriptor d = descriptor("defaultValue", defaultValue,
150                                       "minValue", minValue,
151                                       "maxValue", maxValue);
152             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
153                                                    true, true, false, d),
154                  valid, invalid);
155             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
156                                                    true, true, false,
157                                                    defaultValue,
158                                                    minValue, maxValue),
159                  valid, invalid);
160             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
161                                                    d),
162                  valid, invalid);
163             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
164                                                    defaultValue,
165                                                    minValue, maxValue),
166                  valid, invalid);
167         }
168 
169         if (minValue == null && maxValue == null) {
170             // Legal values in descriptor can be either an array or a set
171             Descriptor d1 = descriptor("defaultValue", defaultValue,
172                                        "legalValues", legalValues);
173             Descriptor d2;
174             if (legalValues == null)
175                 d2 = d1;
176             else {
177                 d2 = descriptor("defaultValue", defaultValue,
178                                 "legalValues", arraySet(legalValues));
179             }
180             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
181                                                    true, true, false, d1),
182                  valid, invalid);
183             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
184                                                    true, true, false, d2),
185                  valid, invalid);
186             test(new OpenMBeanAttributeInfoSupport("name", "descr", openType,
187                                                    true, true, false,
188                                                    defaultValue, legalValues),
189                  valid, invalid);
190             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
191                                                    d1),
192                  valid, invalid);
193             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
194                                                    d2),
195                  valid, invalid);
196             test(new OpenMBeanParameterInfoSupport("name", "descr", openType,
197                                                    defaultValue, legalValues),
198                  valid, invalid);
199         }
200     }
201 
202     /* Test one of the objects.  Note that OpenMBeanAttributeInfo
203        extends OpenMBeanParameterInfo, so OpenMBeanAttributeInfoSupport
204        is-an OpenMBeanParameterInfo.  */
205     private static void test(OpenMBeanParameterInfo info,
206                              Object[] valid, Object[] invalid) {
207         test1(info, valid, invalid);
208 
209         // Check that the constraints can be specified as strings
210         // rather than objects
211         if (info.getOpenType() instanceof SimpleType<?>) {
212             Descriptor d = ((DescriptorRead) info).getDescriptor();
213             String[] names = d.getFieldNames();
214             Object[] values = d.getFieldValues(names);
215             for (int i = 0; i < values.length; i++) {
216                 if (values[i] == null)
217                     continue;
218                 if (names[i].equals("legalValues")) {
219                     Collection<?> legals;
220                     if (values[i] instanceof Collection<?>)
221                         legals = (Collection<?>) values[i];
222                     else
223                         legals = Arrays.asList((Object[]) values[i]);
224                     List<String> strings = new ArrayList<String>();
225                     for (Object legal : legals)
226                         strings.add(legal.toString());
227                     values[i] = strings.toArray(new String[0]);
228                 } else if (!(values[i] instanceof OpenType<?>))
229                     values[i] = values[i].toString();
230             }
231             d = new ImmutableDescriptor(names, values);
232             OpenType<?> ot = info.getOpenType();
233             if (info instanceof OpenMBeanAttributeInfo) {
234                 OpenMBeanAttributeInfo ai = (OpenMBeanAttributeInfo) info;
235                 info = new OpenMBeanAttributeInfoSupport(info.getName(),
236                                                          info.getDescription(),
237                                                          info.getOpenType(),
238                                                          ai.isReadable(),
239                                                          ai.isWritable(),
240                                                          ai.isIs(),
241                                                          d);
242             } else {
243                 info = new OpenMBeanParameterInfoSupport(info.getName(),
244                                                          info.getDescription(),
245                                                          info.getOpenType(),
246                                                          d);
247             }
248             test1(info, valid, invalid);
249         }
250     }
251 
252     private static void test1(OpenMBeanParameterInfo info,
253                               Object[] valid, Object[] invalid) {
254 
255         for (Object x : valid) {
256             if (!info.isValue(x)) {
257                 fail("Object should be valid but is not: " + x + " for: " +
258                      info);
259             }
260         }
261 
262         for (Object x : invalid) {
263             if (info.isValue(x)) {
264                 fail("Object should not be valid but is: " + x + " for: " +
265                      info);
266             }
267         }
268 
269         /* If you specify e.g. minValue in a descriptor, then we arrange
270            for getMinValue() to return the same value, and if you specify
271            a minValue as a constructor parameter then we arrange for the
272            descriptor to have a minValue entry.  Check that these values
273            do in fact match.  */
274 
275         Descriptor d = ((DescriptorRead) info).getDescriptor();
276 
277         checkSameValue("defaultValue", info.getDefaultValue(),
278                        d.getFieldValue("defaultValue"));
279         checkSameValue("minValue", info.getMinValue(),
280                        d.getFieldValue("minValue"));
281         checkSameValue("maxValue", info.getMaxValue(),
282                        d.getFieldValue("maxValue"));
283         checkSameValues("legalValues", info.getLegalValues(),
284                         d.getFieldValue("legalValues"));
285     }
286 
287     private static void checkSameValue(String what, Object getterValue,
288                                        Object descriptorValue) {
289         if (getterValue == null) {
290             if (descriptorValue != null) {
291                 fail("Getter returned null but descriptor has entry for " +
292                      what + ": " + descriptorValue);
293             }
294         } else if (descriptorValue == null) {
295             fail("Getter returned value but descriptor has no entry for " +
296                  what + ": " + getterValue);
297         } else if (!getterValue.equals(descriptorValue) &&
298                    !getterValue.toString().equals(descriptorValue)) {
299             fail("For " + what + " getter returned " + getterValue +
300                  " but descriptor entry is " + descriptorValue);
301         }
302     }
303 
304     private static void checkSameValues(String what, Set<?> getterValues,
305                                         Object descriptorValues) {
306         if (getterValues == null) {
307             if (descriptorValues != null) {
308                 fail("Getter returned null but descriptor has entry for " +
309                      what + ": " + descriptorValues);
310             }
311         } else if (descriptorValues == null) {
312             fail("Getter returned value but descriptor has no entry for " +
313                  what + ": " + getterValues);
314         } else {
315             Set<?> descriptorValueSet;
316             if (descriptorValues instanceof Set<?>)
317                 descriptorValueSet = (Set<?>) descriptorValues;
318             else
319                 descriptorValueSet = arraySet((Object[]) descriptorValues);
320             boolean same = true;
321             if (getterValues.size() != descriptorValueSet.size())
322                 same = false;
323             else {
324                 for (Object x : getterValues) {
325                     if (!descriptorValueSet.contains(x)
326                         && !descriptorValueSet.contains(x.toString())) {
327                         same = false;
328                         break;
329                     }
330                 }
331             }
332             if (!same) {
333                 fail("For " + what + " getter returned " + getterValues +
334                      " but descriptor entry is " + descriptorValueSet);
335             }
336         }
337     }
338 
339     private static void fail(String why) {
340         System.out.println("FAILED: " + why);
341         failure = why;
342     }
343 
344     private static Descriptor descriptor(Object... entries) {
345         if (entries.length % 2 != 0)
346             throw new RuntimeException("Odd length descriptor entries");
347         String[] names = new String[entries.length / 2];
348         Object[] values = new Object[entries.length / 2];
349         for (int i = 0; i < entries.length; i += 2) {
350             names[i / 2] = (String) entries[i];
351             values[i / 2] = entries[i + 1];
352         }
353         return new ImmutableDescriptor(names, values);
354     }
355 
356     private static <T> Set<T> arraySet(T[] array) {
357         return new HashSet<T>(Arrays.asList(array));
358     }
359 
360     private static final OpenType<?>
361         ostring = SimpleType.STRING,
362         oint = SimpleType.INTEGER,
363         obool = SimpleType.BOOLEAN,
364         olong = SimpleType.LONG,
365         obyte = SimpleType.BYTE,
366         ofloat = SimpleType.FLOAT,
367         odouble = SimpleType.DOUBLE,
368         ostringarray, ostringarray2;
369     private static final CompositeType ocomposite;
370     private static final CompositeData compositeData, compositeData2;
371     static {
372         try {
373             ostringarray = new ArrayType<String[]>(1, ostring);
374             ostringarray2 = new ArrayType<String[][]>(2, ostring);
375             ocomposite =
376                 new CompositeType("name", "descr",
377                                   new String[] {"s", "i"},
378                                   new String[] {"sdesc", "idesc"},
379                                   new OpenType[] {ostring, oint});
380             compositeData =
381                 new CompositeDataSupport(ocomposite,
382                                          new String[] {"s", "i"},
383                                          new Object[] {"foo", 23});
384             compositeData2 =
385                 new CompositeDataSupport(ocomposite,
386                                          new String[] {"s", "i"},
387                                          new Object[] {"bar", -23});
388         } catch (OpenDataException e) { // damn checked exceptions...
389             throw new IllegalArgumentException(e.toString(), e);
390         }
391     }
392 
393     private static final Descriptor
394         nullD = null,
395         emptyD = ImmutableDescriptor.EMPTY_DESCRIPTOR;
396 
397     /* The elements of this array are grouped as follows.  Each
398        element contains four Object[]s.  The first one is a set of
399        four values: default value, min value, max value, legal values
400        (an Object[]), some of which can be null.  These will be used
401        to derive the OpenMBean*Info values to be tested.  The second
402        is an array with one element that is the OpenType that will be
403        given to the constructors of the OpenMBean*Infos.  The third
404        element is a set of values that should be valid according to
405        the constraints in the OpenMBean*Info.  The fourth is a set of
406        values that should be invalid according to those
407        constraints.  */
408     private static final Object[][][] tests = {
409 
410         // Test cases when there are no constraints
411         // Validity checking is limited to type of object
412 
413         {{null, null, null, null},
414          {oint},
415          {-1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE},
416          {null, "noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD,
417           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
418 
419         {{null, null, null, null},
420          {obool},
421          {true, false},
422          {null, "noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD,
423           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
424 
425         {{null, null, null, null},
426          {ostring},
427          {"", "yes!"},
428          {null, 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
429           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
430 
431         {{null, null, null, null},
432          {obyte},
433          {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0},
434          {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
435           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
436 
437         {{null, null, null, null},
438          {ostringarray},
439          {new String[0], new String[] {"hello", "world"}},
440          {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
441           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
442 
443         {{null, null, null, null},
444          {ostringarray2},
445          {new String[0][0], new String[][] {{"hello", "world"},
446                                             {"goodbye", "cruel", "world"}}},
447          {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
448           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
449 
450         {{null, null, null, null},
451          {ocomposite},
452          {compositeData, compositeData2},
453          {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
454           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
455 
456         // Test cases where there is a default value, so null is allowed
457 
458         {{23, null, null, null},
459          {oint},
460          {null, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE},
461          {"noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD,
462           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
463 
464         {{true, null, null, null},
465          {obool},
466          {null, true, false},
467          {"noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD,
468           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
469 
470         {{"foo", null, null, null},
471          {ostring},
472          {null, "", "yes!"},
473          {1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
474           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
475 
476         {{(byte) 23, null, null, null},
477          {obyte},
478          {null, Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0},
479          {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
480           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
481 
482         {{compositeData, null, null, null},
483          {ocomposite},
484          {null, compositeData, compositeData2},
485          {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD,
486           new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}},
487 
488         // Test cases where there is a min and/or max, with or without default
489 
490         {{23, 0, 50, null},
491          {oint},
492          {null, 0, 25, 50},
493          {"noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}},
494 
495         {{null, 0, 50, null},
496          {oint},
497          {0, 25, 50},
498          {null, "noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}},
499 
500         {{null, 0, null, null},
501          {oint},
502          {0, 25, 50, Integer.MAX_VALUE},
503          {null, "noddy", -1, Integer.MIN_VALUE, 25L}},
504 
505         {{null, null, 50, null},
506          {oint},
507          {Integer.MIN_VALUE, -1, 0, 25, 50},
508          {null, "noddy", 51, Integer.MAX_VALUE, 25L}},
509 
510         {{"go", "a", "z~", null},
511          {ostring},
512          {null, "a", "z~", "zzzz", "z!"},
513          {"A", "~", "", -1}},
514 
515         // Test cases where there is a set of legal values
516 
517         {{23, null, null, new Integer[] {2, 3, 5, 7, 11, 13, 17, 23}},
518          {oint},
519          {null, 2, 11, 23},
520          {"noddy", -1, 1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}},
521 
522         {{null, null, null, new CompositeData[] {compositeData}},
523          {ocomposite},
524          {compositeData},
525          {null, compositeData2, "noddy"}},
526 
527         {{null, null, null, new Long[0]},
528          {olong},
529          {}, // constraint is impossible to satisfy!
530          {null, 23L, "x", 23}},
531     };
532 }